from xml.dom import minidom

class WidgetOrganizer:
    '''handles propper interacting between parents and children'''
    
    def __init__(self, identifier, ancestor=None):
        #with this identifier, widgets can get their ancestor widgets
        self.identifier = identifier
        
        #here is the proximate ancestor stored
        self.ancestor = ancestor
        
        #all children that should have access to ancestor widgets
        #are stored here
        self.children = []
        
        #this enables to only ask the user if he want's to save if
        #he changed something
        self.changed = False
        
        #their should only be one setting object at any time, so it is created
        #once and then passed on to children (the same for the parsers)
        if self.ancestor:
            self.settings = self.ancestor.settings
            self.parsers = self.ancestor.parsers
            self.executor = self.ancestor.executor
            
        #every widget that has an xmlTag, encapsulates all the xml or dom they
        #get from their children with this tag before they pass it on to their
        #ancestor
        self.xmlTag = ''
        
        #since every widget has this method it is called here automatically
        self.show()        
    
    def addChild(self, child, pos=0):
        '''
        Adds a widget to the children list at Position pos
        
        Every widget in the widget tree has to be registered with this
        function so that interacting with other widgets is possible.
        '''
        
        if pos==-1:
            self.children.append(child)
        else:
            self.children.insert(pos, child)
    
    def getAncestor(self, identifier):
        '''
        Returns the next ancestor with this identifier, or if nothing
        is found returns the widget at top of the widget-tree. 
        '''
        
        if identifier==self.identifier or self.ancestor==None:
            return self
        else:
            return self.ancestor.getAncestor(identifier)
         
    def refresh(self):
        '''
        Passes the refresh signal on to it's children.
        
        If the settings are reloaded, the refresh method of all registered
        objects are called. When the MainWindow gets the signal, it will now 
        propergate down the widget tree so that every widget has the chance 
        to react to changings.
        It just has to override this method.
        '''
        
        for child in self.children:
            child.refresh()
            
    def evaluate(self):
        '''
        Evaluates whatever is possible down the widget tree. Widgets that 
        want to react to that call, have to override this method.
        '''
        
        for child in self.children:
            child.evaluate()
        
                
    def hasChanged(self):
        '''
        Returns True if itself or any of it's children has changed.
        
        It's own state is defined by the changed attribute. If this
        attribute is not set, it is considered as changed=False. The
        state of the children is calculated through envoking their 
        hasChanged method. If any child has changed it returns True.
        ''' 
        
        return any([i.hasChanged() for i in self.children]+[self.changed])
        
    def setChanged(self, changed=False):
        '''
        Sets the changed attribute of all child widgets. 
        
        This for example useful to reset all children, after the
        current state hase be saved. When the user changes anything
        the changed attribute should be True again.
        '''
        
        self.changed = changed
        for child in self.children:
            child.setChanged(changed)
                
    def remove(self):
        '''
        Removes a widget completely.
        
        It invokes the removeChild method of it's ancestor with itself
        as argument. This is necesarry because as long the ancestor keeps
        a reference in his children list the  child won't be garbage-collected
        '''
        
        if self.ancestor:
            self.ancestor.removeChild(self)
        else:
            self.removeChild()

    def removeChild(self, child=None):
        '''
        Removes a child from the children list. If the widget doesn't have this
        child, all children are removed.
        
        Nevertheless it gives the child widget the chance to clean up, by invoking
        it's removeChild method without arguments
        '''
        
        if child and child in self.children:
            child.removeChild()
            child.destroy()
            child.ancestor = None 
            self.children.remove(child)
        else:
            for child in self.children:
                child.removeChild()
                child.destroy()
            self.children = []

    def todom(self, **args):
        '''
        Returns a DOM-object that represents the widget tree below the widget,
        including the widget itself.
        ''' 
        
        if self.xmlTag:
            DOM = minidom.Element(self.xmlTag)
            for child in self.children:
                childDOM = child.todom(**args)
                if childDOM:
                    DOM.appendChild(childDOM)
            return DOM
        elif self.children:
            #if it has no xml-tag it has either only one, or zero children
            #by default it takes the dom-representation of the first child.
            return self.children[0].todom()
        else:
            #There will be many widgets where no information is stored. These
            #don't have to be involved into the dom representation.
            return False
                
    def toxml(self, **args):
        '''
        Returns a xml representation of the widget tree below the widget,
        including the widget itself.
        '''
        
        return self.todom(**args).toxml()
